home *** CD-ROM | disk | FTP | other *** search
/ CD Concept 6 / CD Concept 06.iso / mac / UTILITAIRE / Little Smalltalk v3.1.4 / C Source / Sources / interp.c < prev    next >
Text File  |  1994-10-02  |  19KB  |  598 lines

  1. /*
  2.     Little Smalltalk version 3
  3.     Written by Tim Budd, Oregon State University, July 1988
  4.  
  5.     bytecode interpreter module
  6.  
  7.     given a process object, execute bytecodes in a tight loop.
  8.  
  9.     performs subroutine calls for
  10.         a) finding a non-cached method
  11.         b) executing a primitive
  12.  
  13.     otherwise simply loops until time slice has ended
  14. */
  15.  
  16. # include <stdio.h>
  17. # include "env.h"
  18. # include "memory.h"
  19. # include "names.h"
  20. # include "interp.h"
  21.  
  22. #ifdef THINKC
  23. # include "interp.proto.h"
  24. # include "memory.proto.h"
  25. # include "names.proto.h"
  26. # include "news.proto.h"
  27. # ifdef TCL
  28. #   include "tclprim.proto.h"
  29. # else
  30. #   include "winprim.proto.h"
  31. # endif
  32. #else
  33. static int messTest(object obj);
  34. static boolean findMethod(object *methodClassLocation);
  35. static object growProcessStack(int top, int toadd);
  36.  
  37. static int messTest(object);
  38. static boolean findMethod(object *);
  39. static object growProcessStack(int, int);
  40. static int messTest(object);
  41. static boolean findMethod(object *);
  42. static object growProcessStack(int, int);
  43. #endif
  44.  
  45. object trueobj, falseobj;
  46. boolean watching = 0;
  47. extern object primitive( INT X OBJP );
  48.  
  49. /*
  50.     the following variables are local to this module
  51. */
  52.  
  53. static object method, messageToSend;
  54.  
  55. static int messTest(object obj)
  56. {
  57.     return obj == messageToSend;
  58. }
  59.  
  60. /* a cache of recently executed methods is used for fast lookup */
  61. # define cacheSize 211
  62. static struct {
  63.     object cacheMessage;    /* the message being requested */
  64.     object lookupClass;    /* the class of the receiver */
  65.     object cacheClass;    /* the class of the method */
  66.     object cacheMethod;    /* the method itself */
  67.     } methodCache[cacheSize];
  68.  
  69. /* flush an entry from the cache (usually when its been recompiled) */
  70. void flushCache(object messageToSend, object class)
  71. {    int hash;
  72.  
  73.     hash = (((int) messageToSend) + ((int) class)) / cacheSize;
  74.     methodCache[hash].cacheMessage = nilobj;
  75. }
  76.     
  77. /*
  78.     findMethod
  79.         given a message and a class to start looking in,
  80.         find the method associated with the message
  81. */
  82. static boolean findMethod(object *methodClassLocation)
  83. {    object methodTable, methodClass;
  84.  
  85.     method = nilobj;
  86.     methodClass = *methodClassLocation;
  87.  
  88.     for (; methodClass != nilobj; methodClass = 
  89.             basicAt(methodClass, superClassInClass)) {
  90.         methodTable = basicAt(methodClass, methodsInClass);
  91.         method = hashEachElement(methodTable, messageToSend, messTest);
  92.         if (method != nilobj)
  93.             break;
  94.         }
  95.  
  96.     if (method == nilobj) {        /* it wasn't found */
  97.         methodClass = *methodClassLocation;
  98.         return false;
  99.         }
  100.  
  101.     *methodClassLocation = methodClass;
  102.     return true;
  103. }
  104.  
  105. # define nextByte() *(bp + byteOffset++)
  106. # define ipush(x) incr(*++pst=(x))
  107. # define stackTop() *pst
  108. # define stackTopPut(x) decr((*pst)); incr((*pst = x))
  109. # define stackTopFree() decr((*pst)); *pst-- = nilobj
  110. /* note that ipop leaves x with excess reference count */
  111. # define ipop(x) x = stackTop(); *pst-- = nilobj
  112. # define processStackTop() ((pst-psb)+1)
  113. # define receiverAt(n) *(rcv+n)
  114. # define receiverAtPut(n,x) decr(receiverAt(n)); incr(receiverAt(n)=(x))
  115. # define argumentsAt(n) *(arg+n)
  116. # define temporaryAt(n) *(temps+n)
  117. # define temporaryAtPut(n,x) decr(temporaryAt(n)); incr(temporaryAt(n)=(x))
  118. # define literalsAt(n) *(lits+n)
  119. # define contextAt(n) *(cntx+n)
  120. # define contextAtPut(n,x) incr(contextAt(n-1)=(x))
  121. # define processStackAt(n) *(psb+(n-1))
  122.  
  123.  
  124. /* the following are manipulated by primitives */
  125. object     processStack;
  126. int     linkPointer;
  127.  
  128. static object growProcessStack(int top, int toadd)
  129. {    int size, i;
  130.     object newStack;
  131.  
  132.     if (toadd < 100) toadd = 100;
  133.     size = sizeField(processStack) + toadd;
  134.     newStack = newArray(size);
  135.     for (i = 1; i <= top; i++) {
  136.         basicAtPut(newStack, i, basicAt(processStack, i));
  137.         }
  138.     return newStack;
  139. }
  140.  
  141. boolean execute(object aProcess, int maxsteps)
  142. {   object returnedObject;
  143.     int returnPoint, timeSliceCounter;
  144.     object *pst, *psb, *rcv, *arg, *temps, *lits, *cntx;
  145.     object contextObject, *primargs;
  146.     int byteOffset;
  147.     object methodClass, argarray;
  148.     int i, j;
  149.     register int low;
  150.     int high;
  151.     register object incrobj;  /* speed up increments and decrements */
  152.     byte *bp;
  153.  
  154.     /* unpack the instance variables from the process */
  155.     processStack    = basicAt(aProcess, stackInProcess);
  156.     psb = sysMemPtr(processStack);
  157.     j = intValue(basicAt(aProcess, stackTopInProcess));
  158.     pst = psb + (j-1);
  159.     linkPointer     = intValue(basicAt(aProcess, linkPtrInProcess));
  160.  
  161.     /* set the process time-slice counter before entering loop */
  162.     timeSliceCounter = maxsteps;
  163.  
  164.     /* retrieve current values from the linkage area */
  165.   readLinkageBlock:
  166.     contextObject  = processStackAt(linkPointer+1);
  167.     returnPoint = intValue(processStackAt(linkPointer+2));
  168.     byteOffset  = intValue(processStackAt(linkPointer+4));
  169.     if (contextObject == nilobj) {
  170.         contextObject = processStack;
  171.         cntx = psb;
  172.         arg = cntx + (returnPoint-1);
  173.         method      = processStackAt(linkPointer+3);
  174.         temps = cntx + linkPointer + 4;
  175.     }
  176.     else {    /* read from context object */
  177.         cntx = sysMemPtr(contextObject);
  178.         method = basicAt(contextObject, methodInContext);
  179.         arg = sysMemPtr(basicAt(contextObject, argumentsInContext));
  180.         temps = sysMemPtr(basicAt(contextObject, temporariesInContext));
  181.     }
  182.  
  183.     if (! isInteger(argumentsAt(0)))
  184.         rcv = sysMemPtr(argumentsAt(0));
  185.  
  186.   readMethodInfo:
  187.     lits           = sysMemPtr(basicAt(method, literalsInMethod));
  188.     bp             = bytePtr(basicAt(method, bytecodesInMethod)) - 1;
  189.  
  190.     while ( --timeSliceCounter > 0 ) {
  191.         low = (high = nextByte()) & 0x0F;
  192.         high >>= 4;
  193.         if (high == 0) {
  194.             high = low;
  195.             low = nextByte();
  196.         }
  197.  
  198. # if 1
  199.         if (debugging) {
  200.             fprintf(stderr,"method %s %d ",charPtr(basicAt(method, messageInMethod)), byteOffset);
  201.             fprintf(stderr,"stack %d %d ",pst, *pst);
  202.             fprintf(stderr,"executing %d %d\n", high, low);
  203.         }
  204. # endif
  205.         switch(high) {
  206.  
  207.             case PushInstance:
  208.                 ipush(receiverAt(low));
  209.                 break;
  210.  
  211.             case PushArgument:
  212.                 ipush(argumentsAt(low));
  213.                 break;
  214.  
  215.             case PushTemporary:
  216.                 ipush(temporaryAt(low));
  217.                 break;
  218.  
  219.             case PushLiteral:
  220.                 ipush(literalsAt(low));
  221.                 break;
  222.  
  223.             case PushConstant:
  224.                 switch(low) {
  225.                     case 0: case 1: case 2:
  226.                         ipush(newInteger(low));
  227.                         break;
  228.  
  229.                     case minusOne:
  230.                         ipush(newInteger(-1));
  231.                         break;
  232.  
  233.                     case contextConst:
  234.                         /* check to see if we have made a block context yet */
  235.                         if (contextObject == processStack) {
  236.                             /* not yet, do it now - first get real return point */
  237.                 returnPoint = intValue(processStackAt(linkPointer+2));
  238.                 contextObject = newContext(linkPointer, method,
  239.                 copyFrom(processStack, returnPoint, 
  240.                         linkPointer - returnPoint),
  241.                 copyFrom(processStack, linkPointer + 5,
  242.                         methodTempSize(method)));
  243.                             basicAtPut(processStack, linkPointer+1, contextObject);
  244.                 ipush(contextObject);
  245.                 /* save byte pointer then restore things properly */
  246.                 fieldAtPut(processStack, linkPointer+4, newInteger(byteOffset));
  247.                 goto readLinkageBlock;
  248.  
  249.                             }
  250.                         ipush(contextObject);
  251.                         break;
  252.  
  253.                     case nilConst: 
  254.                         ipush(nilobj);
  255.                         break;
  256.  
  257.                     case trueConst:
  258.                         ipush(trueobj);
  259.                         break;
  260.  
  261.                     case falseConst:
  262.                         ipush(falseobj);
  263.                         break;
  264.  
  265.                     default:
  266.                         sysError("unimplemented constant","pushConstant");
  267.                     }
  268.                 break;
  269.  
  270.             case AssignInstance:
  271.                 receiverAtPut(low, stackTop());
  272.                 break;
  273.  
  274.             case AssignTemporary:
  275.                 temporaryAtPut(low, stackTop());
  276.                 break;
  277.  
  278.             case MarkArguments:
  279.                 returnPoint = (processStackTop() - low) + 1;
  280.                 timeSliceCounter++;    /* make sure we do send */
  281.                 break;
  282.  
  283.             case SendMessage:
  284.                 messageToSend = literalsAt(low);
  285.  
  286.          doSendMessage:
  287.             arg = psb + (returnPoint-1);
  288.             if (isInteger(argumentsAt(0)))
  289.                 /* should fix this later */
  290.                    methodClass = getClass(argumentsAt(0));
  291.             else {
  292.                 rcv = sysMemPtr(argumentsAt(0));
  293.                 methodClass = classField(argumentsAt(0));
  294.             }
  295.  
  296.          doFindMessage:
  297.                 /* look up method in cache */
  298.             i = (((int) messageToSend) + ((int) methodClass)) % cacheSize;
  299.             if ((methodCache[i].cacheMessage == messageToSend) &&
  300.                 (methodCache[i].lookupClass == methodClass)) {
  301.                 method = methodCache[i].cacheMethod;
  302.                 methodClass = methodCache[i].cacheClass;
  303.             }
  304.             else {
  305.                 methodCache[i].lookupClass = methodClass;
  306.                 if (! findMethod(&methodClass)) {
  307.                     /* not found, we invoke a smalltalk method */
  308.                     /* to recover */
  309.                     j = processStackTop() - returnPoint;
  310.                     argarray = newArray(j+1);
  311.                     for (; j >= 0; j--) {
  312.                         ipop(returnedObject);
  313.                         basicAtPut(argarray, j+1, returnedObject);
  314.                         decr(returnedObject);
  315.                     }
  316.                     ipush(basicAt(argarray, 1)); /* push receiver back */
  317.                     ipush(messageToSend);
  318.                     messageToSend = newSymbol("message:notRecognizedWithArguments:");
  319.                     ipush(argarray);
  320.                                     /* try again - if fail really give up */
  321.                     if (! findMethod(&methodClass)) {
  322.                         sysWarn("can't find","error recovery method");
  323.                                     /* just quit */
  324.                         return false;
  325.                     }
  326.                 }
  327.                 methodCache[i].cacheMessage = messageToSend;
  328.                    methodCache[i].cacheMethod = method;
  329.                 methodCache[i].cacheClass = methodClass;
  330.             }
  331.  
  332.             if (watching && (basicAt(method, watchInMethod) != nilobj)) {
  333.                     /* being watched, we send to method itself */
  334.                 j = processStackTop() - returnPoint;
  335.                 argarray = newArray(j+1);
  336.                 for (; j >= 0; j--) {
  337.                     ipop(returnedObject);
  338.                     basicAtPut(argarray, j+1, returnedObject);
  339.                     decr(returnedObject);
  340.                 }
  341.                 ipush(method); /* push method */
  342.                 ipush(argarray);
  343.                 messageToSend = newSymbol("watchWith:");
  344.                             /* try again - if fail really give up */
  345.                 methodClass = classField(method);
  346.                 if (! findMethod(&methodClass)) {
  347.                     sysWarn("can't find","watch method");
  348.                         /* just quit */
  349.                     return false;
  350.                 }
  351.             }
  352.  
  353.                     /* save the current byte pointer */
  354.             fieldAtPut(processStack, linkPointer+4, newInteger(byteOffset));
  355.  
  356.         /* make sure we have enough room in current process */
  357.         /* stack, if not make stack larger */
  358.             i = 6 + methodTempSize(method) + methodStackSize(method);
  359.             j = processStackTop();
  360.             if ((j + i) > sizeField(processStack)) {
  361.                 processStack = growProcessStack(j, i);
  362.                 psb = sysMemPtr(processStack);
  363.                 pst = (psb + j);
  364.                 fieldAtPut(aProcess, stackInProcess, processStack);
  365.             }
  366.  
  367.             byteOffset = 1;
  368.                 /* now make linkage area */
  369.                 /* position 0 : old linkage pointer */
  370.             ipush(newInteger(linkPointer));
  371.             linkPointer = processStackTop();
  372.                 /* position 1 : context object (nil means stack) */
  373.             ipush(nilobj);
  374.             contextObject = processStack;
  375.             cntx = psb;
  376.                 /* position 2 : return point */
  377.             ipush(newInteger(returnPoint));
  378.             arg = cntx + (returnPoint-1);
  379.                 /* position 3 : method */
  380.             ipush(method);
  381.                 /* position 4 : bytecode counter */
  382.             ipush(newInteger(byteOffset));
  383.                 /* then make space for temporaries */
  384.             temps = pst+1;
  385.             pst += methodTempSize(method);
  386.                 /* break if we are too big and probably looping */
  387.             if (sizeField(processStack) > 1800) timeSliceCounter = 0;
  388.                 goto readMethodInfo; 
  389.  
  390.             case SendUnary:
  391.                     /* do isNil and notNil as special cases, since */
  392.                     /* they are so common */
  393.             if ((! watching) && (low <= 1)) {
  394.                 if (stackTop() == nilobj) {
  395.                     stackTopPut((low?falseobj:trueobj));
  396.                     break;
  397.                 }
  398.             }
  399.             returnPoint = processStackTop();
  400.             messageToSend = unSyms[low];
  401.             goto doSendMessage;
  402.             break;
  403.  
  404.         case SendBinary:
  405.                 /* optimized as long as arguments are int */
  406.                 /* and conversions are not necessary */
  407.                 /* and overflow does not occur */
  408.                 if ((! watching) && (low <= 12)) {
  409.                     primargs = pst - 1;
  410.                        returnedObject = primitive(low+60, primargs);
  411.                     if (returnedObject != nilobj) {
  412.                             /* pop arguments off stack , push on result */
  413.                         stackTopFree();
  414.                         stackTopPut(returnedObject);
  415.                         break;
  416.                     }
  417.                 }
  418.                 /* else we do it the old fashion way */
  419.                 returnPoint = processStackTop() - 1;
  420.                 messageToSend = binSyms[low];
  421.                 goto doSendMessage;
  422.  
  423.         case DoPrimitive:
  424.         /* low gives number of arguments */
  425.         /* next byte is primitive number */
  426.             primargs = (pst - low) + 1;
  427.         /* next byte gives primitive number */
  428.             i = nextByte();
  429.         /* a few primitives are so common, and so easy, that
  430.            they deserve special treatment */
  431.             switch(i) {
  432.                 case 5:    /* set watch */
  433.                     watching = ! watching;
  434.                     returnedObject = watching?trueobj:falseobj;
  435.                     break;
  436.  
  437.                 case 11: /* class of object */
  438.                     returnedObject = getClass(*primargs);
  439.                     break;
  440.                 case 21: /* object equality test */
  441.                     if (*primargs == *(primargs+1))
  442.                        returnedObject = trueobj;
  443.                     else returnedObject = falseobj;
  444.                     break;
  445.                 case 25: /* basicAt: */
  446.                     j = intValue(*(primargs+1));
  447.                     returnedObject = basicAt(*primargs, j);
  448.                     break;
  449.                 case 31: /* basicAt:Put:*/
  450.                     j = intValue(*(primargs+1));
  451.                     fieldAtPut(*primargs, j, *(primargs+2));
  452.                     returnedObject = nilobj;
  453.                     break;
  454.                 case 53: /* set time slice */
  455.                     timeSliceCounter = intValue(*primargs);
  456.                     returnedObject = nilobj;
  457.                     break;
  458.                 case 58: /* allocObject */
  459.                     j = intValue(*primargs);
  460.                     returnedObject = allocObject(j);
  461.                     break;
  462.                 case 87: /* value of symbol */
  463.                     returnedObject = globalSymbol(charPtr(*primargs));
  464.                     break;
  465.                 default: 
  466.                     returnedObject = primitive(i, primargs); break;
  467.             }
  468.         /* increment returned object in case pop would destroy it */
  469.             incr(returnedObject);
  470.         /* pop off arguments */
  471.             while (low-- > 0) {
  472.                 stackTopFree();
  473.             }
  474.         /* returned object has already been incremented */
  475.             ipush(returnedObject); decr(returnedObject);
  476.             break;
  477.  
  478.             doReturn:
  479.             returnPoint = intValue(basicAt(processStack, linkPointer + 2));
  480.             linkPointer = intValue(basicAt(processStack, linkPointer));
  481.             while (processStackTop() >= returnPoint) {
  482.                 stackTopFree();
  483.             }
  484.         /* returned object has already been incremented */
  485.             ipush(returnedObject); decr(returnedObject);
  486.         /* now go restart old routine */
  487.             if (linkPointer != nilobj)
  488.                 goto readLinkageBlock;
  489.             else
  490.                 return false /* all done */;
  491.  
  492.             case DoSpecial:
  493.                 switch(low) {
  494.                     case SelfReturn:
  495.                         incr(returnedObject = argumentsAt(0));
  496.                         goto doReturn;
  497.     
  498.                     case StackReturn:
  499.                         ipop(returnedObject);
  500.                         goto doReturn;
  501.     
  502.                     case Duplicate:
  503.                 /* avoid possible subtle bug */
  504.                         returnedObject = stackTop();
  505.                         ipush(returnedObject);
  506.                         break;
  507.     
  508.                     case PopTop:
  509.                         ipop(returnedObject);
  510.                         decr(returnedObject);
  511.                         break;
  512.     
  513.                     case Branch:
  514.                         /* avoid a subtle bug here */
  515.                         i = nextByte();
  516.                         byteOffset = i;
  517.                         break;
  518.     
  519.                     case BranchIfTrue:
  520.                         ipop(returnedObject);
  521.                         i = nextByte();
  522.                         if (returnedObject == trueobj) {
  523.                                 /* leave nil on stack */
  524.                             pst++;
  525.                             byteOffset = i;
  526.                         }
  527.                         decr(returnedObject);
  528.                         break;
  529.     
  530.                     case BranchIfFalse:
  531.                         ipop(returnedObject);
  532.                         i = nextByte();
  533.                         if (returnedObject == falseobj) {
  534.                             /* leave nil on stack */
  535.                             pst++;
  536.                             byteOffset = i;
  537.                         }
  538.                         decr(returnedObject);
  539.                         break;
  540.     
  541.                     case AndBranch:
  542.                         ipop(returnedObject);
  543.                         i = nextByte();
  544.                         if (returnedObject == falseobj) {
  545.                             ipush(returnedObject);
  546.                             byteOffset = i;
  547.                         }
  548.                         decr(returnedObject);
  549.                         break;
  550.     
  551.                     case OrBranch:
  552.                         ipop(returnedObject);
  553.                         i = nextByte();
  554.                         if (returnedObject == trueobj) {
  555.                             ipush(returnedObject);
  556.                             byteOffset = i;
  557.                         }
  558.                         decr(returnedObject);
  559.                         break;
  560.     
  561.                     case SendToSuper:
  562.                         i = nextByte();
  563.                         messageToSend = literalsAt(i);
  564.                         rcv = sysMemPtr(argumentsAt(0));
  565.                         methodClass = basicAt(method, methodClassInMethod);
  566.                         /* if there is a superclass, use it
  567.                            otherwise for class Object (the only 
  568.                            class that doesn't have a superclass) use
  569.                            the class again */
  570.                         returnedObject = 
  571.                                   basicAt(methodClass, superClassInClass);
  572.                         if (returnedObject != nilobj)
  573.                             methodClass = returnedObject;
  574.                             goto doFindMessage;
  575.     
  576.                     default:
  577.                         sysError("invalid doSpecial","");
  578.                         break;
  579.                 }
  580.                 break;
  581.  
  582.             default:
  583.                 sysError("invalid bytecode","");
  584.                 break;
  585.         }
  586.     }
  587.  
  588.     /* before returning we put back the values in the current process */
  589.     /* object */
  590.  
  591.     fieldAtPut(processStack, linkPointer+4, newInteger(byteOffset));
  592.     fieldAtPut(aProcess, stackTopInProcess, newInteger(processStackTop()));
  593.     fieldAtPut(aProcess, linkPtrInProcess, newInteger(linkPointer));
  594.  
  595.     return true;
  596. }
  597.  
  598.